package br.com.afi.util.managerEmail;

import java.io.File;
import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import java.util.Map.Entry;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.Message.RecipientType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import br.com.afi.util.log.Logger;

/**
 * Essa classe autêntica no servidor SMTP, envia e-mails e possui outros métodos úteis para e-mails.
 * 
 * @author André de Fontana Ignacio
 * @version 1.0
 */
public class ManagerEmail
{
	private static final Logger log = Logger.getLogger( ManagerEmail.class );
	private String smtpServer;
	private String user;
	private String pass;
	private int options[];

	/**
	 * Utilize essa opção para indicar que a autenticação no servidor será realizada utilizando TLS
	 */
	public static final int TLS = 1;

	/**
	 * Construtor
	 * 
	 * @param smtpServer Endereço do servidor SMTP
	 * @param options Opções, se necessário utilize as constantes definidas nesta classe
	 */
	public ManagerEmail( String smtpServer, int... options )
	{
		this.smtpServer = smtpServer;
		this.options = options;
	}

	/**
	 * Construtor
	 */
	public ManagerEmail()
	{
		super();
	}

	public void setSmtpServer( String smtpServer )
	{
		this.smtpServer = smtpServer;
	}

	public void setLoginPass( String user, String pass )
	{
		this.user = user;
		this.pass = pass;
	}

	/**
	 * Obtém o dominio de um endereço de e-mail ex: joao@xyz.com.br, o dominio é xyz.com.br
	 * 
	 * @param emailAddress Endereço de e-mail
	 * @return Domínio
	 */
	public static String getDomain( String emailAddress )
	{
		String domain = null;
		if ( emailAddress != null && emailAddress.length() > 0 )
		{
			final int indexArroba = emailAddress.indexOf( '@' );
			if ( indexArroba != -1 )
			{
				if ( indexArroba + 1 > emailAddress.length() )
				{
					domain = "";
				}
				else
				{
					domain = emailAddress.substring( indexArroba + 1, emailAddress.length() );
				}
			}
		}
		return domain;
	}

	/**
	 * Verifica se existe a opção informada
	 * 
	 * @param option Opção, utilize as constantes definidas nesta classe.
	 * @return Verdadeiro se a opção tiver sido atribuída
	 */
	private boolean hasOption( int option )
	{
		boolean exists = false;
		if ( options != null )
		{
			for ( int o : options )
			{
				if ( o == option )
				{
					exists = true;
					break;
				}
			}
		}
		return exists;
	}

	/**
	 * Atribui opções, utilize as constantes definidas nesta classe.
	 * 
	 * @param options Opções, utilize as constantes definidas nesta classe.
	 */
	public void setOptions( int[] options )
	{
		this.options = options;
	}

	/**
	 * Envia o e-mail informado.
	 * 
	 * @param email E-mail
	 * @throws AuthenticationFailedException Lançada quando não for possível autenticar no servidor
	 * @throws EmailException Lançada quando ocorrer um problema ao enviar o e-mail
	 * @throws InvalidEmailAddressException Lançada quando o endereço do e-mail não existir
	 */
	public void sendEmail( Email email ) throws AuthenticationFailedException, EmailException, InvalidEmailAddressException
	{
		final boolean tls = this.hasOption( TLS );
		Session session = null;
		final Properties props = new Properties();
		props.setProperty( "mail.smtp.host", smtpServer );
		if ( tls )
		{
			props.setProperty( "mail.transport.protocol", "smtps" );
			props.setProperty( "mail.smtp.starttls.enable", "true" );
		}
		Authenticator auth = null;
		if ( user != null )
		{
			props.put( "mail.smtp.auth", "true" );
			auth = new EmailAuthenticator( user, pass );
			session = Session.getInstance( props, auth );
		}
		else
		{
			session = Session.getInstance( props, null );
		}
		final MimeMessage msg = new MimeMessage( session );
		final Multipart multiPart = new MimeMultipart();
		final BodyPart body = new MimeBodyPart();
		try
		{
			final File anexoConteudo = email.getAnexoConteudo();
			if ( anexoConteudo != null )
			{
				final DataSource source = new FileDataSource( anexoConteudo );
				body.setDataHandler( new DataHandler( source ) );
				body.setFileName( anexoConteudo.getName() );
			}
			else
			{
				body.setContent( email.getConteudo(), email.getMimeType() );
			}
			multiPart.addBodyPart( body );

			for ( Entry<String, File> e : email.getAnexos().entrySet() )
			{
				final String nomeArquivo = e.getKey();
				final File arquivo = e.getValue();
				final BodyPart anexo = new MimeBodyPart();
				DataSource source = new FileDataSource( arquivo );
				anexo.setDataHandler( new DataHandler( source ) );
				anexo.setFileName( nomeArquivo );
				multiPart.addBodyPart( anexo );
			}

			msg.setFrom( email.getRemetente() );
			msg.setSubject( email.getAssunto(), "iso-8859-1" );
			msg.setContent( multiPart );
			msg.setSentDate( new Date() );

			final Iterator<InternetAddress> i = email.getDestinatarios().iterator();
			final Iterator<RecipientType> j = email.getTipoDests().iterator();
			final StringBuilder sb = new StringBuilder();
			while ( i.hasNext() )
			{
				final RecipientType t = ( RecipientType ) j.next();
				final InternetAddress ia = ( InternetAddress ) i.next();
				sb.append( ia.getAddress() + "," );
				msg.addRecipient( t, ia );
			}

			Transport.send( msg );

			final String d = sb.toString();
			log.info( "E-mail para {0} enviado com sucesso. ", d.substring( 0, d.length() - 1 ) );
		}
		catch ( javax.mail.AuthenticationFailedException e )
		{
			final AuthenticationFailedException ex = new AuthenticationFailedException( "Usuário e/ou senha inválidos", e );
			log.warn( ex.getMessage(), ex );
			throw ex;
		}
		catch ( SendFailedException e )
		{
			final InvalidEmailAddressException ex = new InvalidEmailAddressException( "Não foi possível enviar o e-mail, provavelmente o domínio não existe.", e );
			log.error( ex.getMessage(), ex );
			throw ex;
		}
		catch ( MessagingException e )
		{
			final EmailException ex = new EmailException( "Não foi possível enviar o e-mail.", e );
			log.error( ex.getMessage(), ex );
			throw ex;
		}
	}
}
